package com.cnhis.drgs.mybatis;

import com.alibaba.druid.sql.SQLUtils;
import com.cnhis.drgs.vo.Page;
import org.apache.commons.lang.reflect.FieldUtils;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 分页插件
 *
 * @author andLevy
 * @date 2018-11-01 10:59:04
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class PageInterceptor implements Interceptor {

    // 数据库类型
    private String dialect;
    // 是否打印sql
    private boolean show_sql;
    // 是否格式化sql
    private boolean format_sql;

    /**
     * 拦截后要执行的方法
     */
    public Object intercept(Invocation invocation) throws Throwable {

        try {

            if (!"mysql".equalsIgnoreCase(dialect)) {
                throw new RuntimeException("该插件只适用MySql数据库");
            }

            RoutingStatementHandler handler = (RoutingStatementHandler) invocation.getTarget();
            BoundSql boundSql = handler.getBoundSql();
            ParameterHandler parameterHandler = handler.getParameterHandler();
            Object parameter = boundSql.getParameterObject();
            String sql = boundSql.getSql();

            //System.out.println("参数：" + parameter);
            //System.out.println("SQL：" + sql);

            if (show_sql) {
                sql = SQLUtils.formatMySql(sql);
            }

            if (show_sql && format_sql) {
                // 去掉换行符、制表符
                Pattern p = Pattern.compile("\n|\r|\t");
                Matcher m = p.matcher(sql);
                sql = m.replaceAll(" ").trim();
            }

            if (parameter instanceof Page) {
                Page page = (Page) parameter;
                // 获取总记录数
                String countSql = this.concatCountSql2(sql);
                if (show_sql) {
                    System.out.println(countSql);
                }
                Connection connection = (Connection) invocation.getArgs()[0];
                PreparedStatement pstmt = connection.prepareStatement(countSql);
                parameterHandler.setParameters(pstmt);
                ResultSet rs = pstmt.executeQuery();
                int count = 0;
                if (rs.next()) {
                    count = rs.getInt(1);
                }
                try {
                    rs.close();
                    pstmt.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                page.setRowCount(count);

                // 附加分页控制
                sql += " LIMIT " + page.getOffset() + "," + page.getPageSize();
            }

            if (show_sql) {
                System.out.println(sql);
            }

            // 设置sql
            FieldUtils.writeDeclaredField(boundSql, "sql", sql, true);
            return invocation.proceed();
        } catch (Exception e) {
            throw e;
        }
    }

    private String concatCountSql2(String sql) {
        return "SELECT COUNT(1) FROM (" + sql + ") t";
    }

    /**
     * 拦截器对应的封装原始对象的方法
     */
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    /**
     * 设置注册拦截器时设定的属性
     */
    public void setProperties(Properties properties) {
        this.dialect = properties.getProperty("dialect");
        try {
            this.show_sql = Boolean.parseBoolean(properties.getProperty("show_sql"));
            this.format_sql = Boolean.parseBoolean(properties.getProperty("format_sql"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}